package com.icongyou.enterprise.course_detail.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.icongyou.enterprise.course_detail.entity.dto.CourseDetailRequestDTO;
import com.icongyou.enterprise.course_detail.entity.vo.*;
import com.icongyou.enterprise.course_detail.service.CourseDetailService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

/**
 * 课程详情 Controller 层测试
 * 测试 API 端点、请求参数验证、响应格式等
 */
@SpringBootTest
@AutoConfigureMockMvc
@DisplayName("课程详情Controller测试")
public class CourseDetailControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    private CourseDetailService courseDetailService;

    private static final String TEST_STUDENT_ID = "660e8400-e29b-41d4-a716-446655440101";
    private static final String TEST_COURSE_ID = "aa0e8400-e29b-41d4-a716-446655440501";

    @Test
    @DisplayName("POST /api/course-detail/get - 成功获取课程详情")
    void testGetCourseDetail_Success() throws Exception {
        // 准备请求数据
        CourseDetailRequestDTO requestDTO = new CourseDetailRequestDTO();
        requestDTO.setStudentId(TEST_STUDENT_ID);
        requestDTO.setCourseId(TEST_COURSE_ID);

        // 准备响应数据
        CourseDetailVO mockResponse = createMockCourseDetailVO();
        when(courseDetailService.getCourseDetail(TEST_STUDENT_ID, TEST_COURSE_ID))
                .thenReturn(mockResponse);

        // 执行请求并验证
        mockMvc.perform(post("/api/course-detail/get")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(requestDTO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.courseId").value(TEST_COURSE_ID))
                .andExpect(jsonPath("$.data.studentId").value(TEST_STUDENT_ID))
                .andExpect(jsonPath("$.data.courseName").value("软件工程实践"))
                .andExpect(jsonPath("$.data.studentName").value("张三"))
                .andExpect(jsonPath("$.data.totalScore").value(88.5))
                .andExpect(jsonPath("$.data.courseRank").value(5))
                .andExpect(jsonPath("$.data.assignments").isArray())
                .andExpect(jsonPath("$.data.assignments.length()").value(2));
    }

    @Test
    @DisplayName("POST /api/course-detail/get - 参数校验失败（studentId为空）")
    void testGetCourseDetail_ValidationFailed_EmptyStudentId() throws Exception {
        CourseDetailRequestDTO requestDTO = new CourseDetailRequestDTO();
        requestDTO.setStudentId(""); // 空字符串
        requestDTO.setCourseId(TEST_COURSE_ID);

        mockMvc.perform(post("/api/course-detail/get")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(requestDTO)))
                .andExpect(status().isBadRequest());
    }

    @Test
    @DisplayName("POST /api/course-detail/get - 参数校验失败（courseId为空）")
    void testGetCourseDetail_ValidationFailed_EmptyCourseId() throws Exception {
        CourseDetailRequestDTO requestDTO = new CourseDetailRequestDTO();
        requestDTO.setStudentId(TEST_STUDENT_ID);
        requestDTO.setCourseId(""); // 空字符串

        mockMvc.perform(post("/api/course-detail/get")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(requestDTO)))
                .andExpect(status().isBadRequest());
    }

    @Test
    @DisplayName("POST /api/course-detail/get - 参数校验失败（两个参数都为空）")
    void testGetCourseDetail_ValidationFailed_BothEmpty() throws Exception {
        CourseDetailRequestDTO requestDTO = new CourseDetailRequestDTO();
        requestDTO.setStudentId(null);
        requestDTO.setCourseId(null);

        mockMvc.perform(post("/api/course-detail/get")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(requestDTO)))
                .andExpect(status().isBadRequest());
    }

    @Test
    @DisplayName("POST /api/course-detail/get - 服务层抛出异常")
    void testGetCourseDetail_ServiceException() throws Exception {
        CourseDetailRequestDTO requestDTO = new CourseDetailRequestDTO();
        requestDTO.setStudentId(TEST_STUDENT_ID);
        requestDTO.setCourseId(TEST_COURSE_ID);

        when(courseDetailService.getCourseDetail(TEST_STUDENT_ID, TEST_COURSE_ID))
                .thenThrow(new RuntimeException("数据库连接失败"));

        mockMvc.perform(post("/api/course-detail/get")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(requestDTO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(500))
                .andExpect(jsonPath("$.message").value("获取课程详情失败: 数据库连接失败"));
    }

    @Test
    @DisplayName("POST /api/course-detail/get - 无效的JSON格式")
    void testGetCourseDetail_InvalidJsonFormat() throws Exception {
        String invalidJson = "{studentId: '20210001', courseId: }"; // 无效JSON

        mockMvc.perform(post("/api/course-detail/get")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(invalidJson))
                .andExpect(status().isBadRequest());
    }

    @Test
    @DisplayName("GET /api/course-detail/get/{studentId}/{courseId} - 成功获取课程详情")
    void testGetCourseDetailByPath_Success() throws Exception {
        CourseDetailVO mockResponse = createMockCourseDetailVO();
        when(courseDetailService.getCourseDetail(TEST_STUDENT_ID, TEST_COURSE_ID))
                .thenReturn(mockResponse);

        mockMvc.perform(get("/api/course-detail/get/{studentId}/{courseId}", TEST_STUDENT_ID, TEST_COURSE_ID))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.courseId").value(TEST_COURSE_ID))
                .andExpect(jsonPath("$.data.studentId").value(TEST_STUDENT_ID))
                .andExpect(jsonPath("$.data.courseName").value("软件工程实践"))
                .andExpect(jsonPath("$.data.totalScore").value(88.5));
    }

    @Test
    @DisplayName("GET /api/course-detail/get/{studentId}/{courseId} - 服务层抛出异常")
    void testGetCourseDetailByPath_ServiceException() throws Exception {
        when(courseDetailService.getCourseDetail(anyString(), anyString()))
                .thenThrow(new RuntimeException("课程不存在"));

        mockMvc.perform(get("/api/course-detail/get/{studentId}/{courseId}", TEST_STUDENT_ID, TEST_COURSE_ID))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(500))
                .andExpect(jsonPath("$.message").value("获取课程详情失败: 课程不存在"));
    }

    @Test
    @DisplayName("GET /api/course-detail/get/{studentId}/{courseId} - 特殊字符处理")
    void testGetCourseDetailByPath_SpecialCharacters() throws Exception {
        String specialStudentId = "stu@123";
        String specialCourseId = "CS-101";

        CourseDetailVO mockResponse = createMockCourseDetailVO();
        when(courseDetailService.getCourseDetail(specialStudentId, specialCourseId))
                .thenReturn(mockResponse);

        mockMvc.perform(get("/api/course-detail/get/{studentId}/{courseId}", specialStudentId, specialCourseId))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

    @Test
    @DisplayName("POST /api/course-detail/get - 返回数据结构完整性检查")
    void testGetCourseDetail_ResponseStructureCompleteness() throws Exception {
        CourseDetailRequestDTO requestDTO = new CourseDetailRequestDTO();
        requestDTO.setStudentId(TEST_STUDENT_ID);
        requestDTO.setCourseId(TEST_COURSE_ID);

        CourseDetailVO mockResponse = createMockCourseDetailVO();
        when(courseDetailService.getCourseDetail(TEST_STUDENT_ID, TEST_COURSE_ID))
                .thenReturn(mockResponse);

        mockMvc.perform(post("/api/course-detail/get")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(requestDTO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.courseId").exists())
                .andExpect(jsonPath("$.data.studentId").exists())
                .andExpect(jsonPath("$.data.courseName").exists())
                .andExpect(jsonPath("$.data.studentName").exists())
                .andExpect(jsonPath("$.data.totalScore").exists())
                .andExpect(jsonPath("$.data.courseRank").exists())
                .andExpect(jsonPath("$.data.assignments").exists())
                .andExpect(jsonPath("$.data.rankTrend").exists())
                .andExpect(jsonPath("$.data.teamContribution").exists())
                // 检查任务详情结构
                .andExpect(jsonPath("$.data.assignments[0].assignmentId").exists())
                .andExpect(jsonPath("$.data.assignments[0].assignmentName").exists())
                .andExpect(jsonPath("$.data.assignments[0].score").exists())
                .andExpect(jsonPath("$.data.assignments[0].contributionRate").exists())
                .andExpect(jsonPath("$.data.assignments[0].assignmentRank").exists())
                .andExpect(jsonPath("$.data.assignments[0].coreAchievements").exists());
    }

    @Test
    @DisplayName("POST /api/course-detail/get - 大量数据处理测试")
    void testGetCourseDetail_LargeDataSet() throws Exception {
        CourseDetailRequestDTO requestDTO = new CourseDetailRequestDTO();
        requestDTO.setStudentId(TEST_STUDENT_ID);
        requestDTO.setCourseId(TEST_COURSE_ID);

        CourseDetailVO mockResponse = createLargeMockCourseDetailVO(50); // 50个任务
        when(courseDetailService.getCourseDetail(TEST_STUDENT_ID, TEST_COURSE_ID))
                .thenReturn(mockResponse);

        mockMvc.perform(post("/api/course-detail/get")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(requestDTO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.assignments.length()").value(50));
    }

    @Test
    @DisplayName("POST /api/course-detail/get - Content-Type检查")
    void testGetCourseDetail_WrongContentType() throws Exception {
        CourseDetailRequestDTO requestDTO = new CourseDetailRequestDTO();
        requestDTO.setStudentId(TEST_STUDENT_ID);
        requestDTO.setCourseId(TEST_COURSE_ID);

        mockMvc.perform(post("/api/course-detail/get")
                        .contentType(MediaType.APPLICATION_XML) // 错误的 Content-Type
                        .content(objectMapper.writeValueAsString(requestDTO)))
                .andExpect(status().isUnsupportedMediaType());
    }

    /**
     * 创建模拟的课程详情VO
     */
    private CourseDetailVO createMockCourseDetailVO() {
        CourseDetailVO vo = new CourseDetailVO();
        vo.setCourseId(TEST_COURSE_ID);
        vo.setStudentId(TEST_STUDENT_ID);
        vo.setCourseName("软件工程实践");
        vo.setStudentName("张三");
        vo.setTotalScore(88.5);
        vo.setCourseRank(5);

        // 添加任务列表
        List<AssignmentCardVO> assignments = new ArrayList<>();
        
        // 个人任务
        AssignmentCardVO assignment1 = new AssignmentCardVO();
        assignment1.setAssignmentId("ee0e8400-e29b-41d4-a716-446655440901");
        assignment1.setAssignmentName("第一周个人作业：环境搭建");
        assignment1.setAssignmentType(0);
        assignment1.setScore(new BigDecimal("88.50"));
        assignment1.setContributionRate(new BigDecimal("100"));
        assignment1.setAssignmentRank(3);
        assignment1.setIsLate(false);
        assignment1.setCoreAchievements(new ArrayList<>());
        assignments.add(assignment1);

        // 团队任务
        AssignmentCardVO assignment2 = new AssignmentCardVO();
        assignment2.setAssignmentId("ee0e8400-e29b-41d4-a716-446655440902");
        assignment2.setAssignmentName("第二周团队作业：需求分析");
        assignment2.setAssignmentType(1);
        assignment2.setScore(new BigDecimal("22.50"));
        assignment2.setContributionRate(new BigDecimal("25.00"));
        assignment2.setAssignmentRank(8);
        assignment2.setIsLate(false);
        assignment2.setCoreAchievements(new ArrayList<>());
        assignments.add(assignment2);

        vo.setAssignments(assignments);

        // 添加排名趋势
        List<RankTrendPointVO> rankTrend = new ArrayList<>();
        RankTrendPointVO point1 = new RankTrendPointVO();
        point1.setAssignmentId("ee0e8400-e29b-41d4-a716-446655440901");
        point1.setAssignmentName("第一周个人作业：环境搭建");
        point1.setRank(3);
        point1.setTotalParticipants(7);
        rankTrend.add(point1);
        vo.setRankTrend(rankTrend);

        // 添加团队贡献度
        TeamContributionVO teamContribution = new TeamContributionVO();
        teamContribution.setTeamContribution(new BigDecimal("25.00"));
        teamContribution.setTeamTaskCount(1);
        teamContribution.setMemberContributions(new ArrayList<>());
        vo.setTeamContribution(teamContribution);

        return vo;
    }

    /**
     * 创建大量数据的模拟课程详情VO
     */
    private CourseDetailVO createLargeMockCourseDetailVO(int assignmentCount) {
        CourseDetailVO vo = createMockCourseDetailVO();
        
        List<AssignmentCardVO> assignments = new ArrayList<>();
        for (int i = 1; i <= assignmentCount; i++) {
            AssignmentCardVO assignment = new AssignmentCardVO();
            assignment.setAssignmentId("a" + i);
            assignment.setAssignmentName("任务" + i);
            assignment.setAssignmentType(i % 2);
            assignment.setScore(new BigDecimal("80.00"));
            assignment.setContributionRate(new BigDecimal("100"));
            assignment.setAssignmentRank(i);
            assignment.setIsLate(false);
            assignment.setCoreAchievements(new ArrayList<>());
            assignments.add(assignment);
        }
        
        vo.setAssignments(assignments);
        return vo;
    }
}
